package com.frank.loadinglibrary.common.ring;

import com.frank.loadinglibrary.common.BaseLoadingComponent;
import com.frank.loadinglibrary.utils.interpolator.FastOutSlowInInterpolator;
import com.frank.loadinglibrary.utils.Indicator;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Arc;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;

import java.util.ArrayList;

public class WhorArcLoadingComponent extends BaseLoadingComponent implements Component.DrawTask {

    private static final int DEGREE_180 = 180;
    private static final int DEGREE_360 = 360;
    private FastOutSlowInInterpolator interpolator = new FastOutSlowInInterpolator();
    //单次绘制画弧所占角度
    private static final float MAX_SWIPE_DEGREES = 0.6f * DEGREE_360;
    //记录三条轨迹的颜色值数组
    private static final Color[] DEFAULT_COLORS = new Color[]{
            Color.RED, Color.GREEN, Color.BLUE
    };

    private Color[] mColors = DEFAULT_COLORS;
    //画布旋转角度
    private float mCanvasRotation;
    //终点角度
    private float mEndDegrees;
    //起点角度
    private float mStartDegrees;
    //扫过角度
    private float mSwipeDegrees;
    //起点绘制结束时进度
    private static final float START_TRIM_DURATION_OFFSET = 0.5f;
    //终点绘制结束时进度
    private static final float END_TRIM_DURATION_OFFSET = 1.0f;
    //上一次的终点点角度
    private float mOriginEndDegrees = 0;
    //上一次的起点角度
    private float mOriginStartDegrees = 0;

    private float mOriginRotateDegrees = 0;

    private final Paint mPaint = new Paint();
    private float mStrokeWidth = 15;
    private RectFloat mTempBounds;
    private final RectFloat mTempArcBounds = new RectFloat();
    private float mMaginInset = 20;

    private Indicator indicator;

    public WhorArcLoadingComponent(Context context) {
        this(context, null);
    }

    public WhorArcLoadingComponent(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    public WhorArcLoadingComponent(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init();
    }

    public WhorArcLoadingComponent(Context context, AttrSet attrSet, int resId) {
        super(context, attrSet, resId);
        init();
    }


    private void init() {
        setPaint();
        setDurtion(2000);
        setBindStateChangedListener(new BindStateChangedListener() {
            @Override
            public void onComponentBoundToWindow(Component component) {
                if (autoStart)
                    startAnimators();
            }

            @Override
            public void onComponentUnboundFromWindow(Component component) {
                stopAnimators();
            }
        });
        indicator = new Indicator<AnimatorValue>() {
            @Override
            public ArrayList<AnimatorValue> onCreateAnimators() {
                ArrayList<AnimatorValue> list = new ArrayList<>();
                AnimatorValue animatorValue = new AnimatorValue();
                animatorValue.setLoopedCount(-1);
                animatorValue.setDuration(durtion);
                indicator.addUpdateListener(animatorValue, (animatorValue1, v) -> {
                    computeDrawParams(v);
                    WhorArcLoadingComponent.this.invalidate();
                });
                animatorValue.setLoopedListener(new Animator.LoopedListener() {
                    @Override
                    public void onRepeat(Animator animator) {
                        resetOriginals();
                        mStartDegrees = mEndDegrees;
                        mOriginRotateDegrees = mCanvasRotation;
                    }
                });
                list.add(animatorValue);
                return list;
            }
        };
        addDrawTask(this);
    }

    @Override
    protected void stopAnimators() {
        if (indicator != null && indicator.isRunning()) {
            indicator.stop();
        }
    }

    @Override
    protected void startAnimators() {
        if (indicator != null && !indicator.isRunning()) {
            indicator.start();
        }
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        int savecount = canvas.save();
        if (mTempBounds == null) {
            mTempBounds = new RectFloat();
            int width = Math.min(getWidth(), getHeight());
            mTempBounds.modify((getWidth() - width) / 2 + mMaginInset, (getHeight() - width) / 2 + mMaginInset, (getWidth() - width) / 2 - mMaginInset + width, (getHeight() - width) / 2 + width - mMaginInset);
        }
        canvas.rotate(mCanvasRotation, getWidth() / 2, getHeight() / 2);

        if (mSwipeDegrees != 0) {
            for (int i = 0; i < mColors.length; i++) {
                mPaint.setStrokeWidth(mStrokeWidth / (i + 1));
                mPaint.setColor(mColors[i]);
                canvas.drawArc(createArcBounds(mTempBounds, i), new Arc(mStartDegrees + DEGREE_180 * (i % 2),
                        mSwipeDegrees, false), mPaint);
            }
        }
        canvas.restoreToCount(savecount);
    }

    private void computeDrawParams(float value) {
        if (value <= START_TRIM_DURATION_OFFSET) {

            float startTrimProgress = value / (1.0f - value);

            mStartDegrees = mOriginStartDegrees + MAX_SWIPE_DEGREES * interpolator.getInterpolation(startTrimProgress);
        }
        if (value > START_TRIM_DURATION_OFFSET) {
            float endTrimProgress = (value - START_TRIM_DURATION_OFFSET) / START_TRIM_DURATION_OFFSET;
            mEndDegrees = mOriginEndDegrees + MAX_SWIPE_DEGREES * interpolator.getInterpolation(endTrimProgress);
        }
        if (Math.abs(mEndDegrees - mStartDegrees) > 0) {
            mSwipeDegrees = mEndDegrees - mStartDegrees;
        }
        mCanvasRotation = (mOriginRotateDegrees + 216 * value) % 360;
    }

    private void setPaint() {
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(mStrokeWidth);
        mPaint.setStyle(Paint.Style.STROKE_STYLE);
        mPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
    }

    private RectFloat createArcBounds(RectFloat sourceArcBounds, int index) {
        int intervalWidth = 0;

        for (int i = 0; i < index; i++) {
            intervalWidth += mStrokeWidth / (i + 1.0f) * 1.5f;
        }

        int arcBoundsLeft = (int) (sourceArcBounds.left + intervalWidth);
        int arcBoundsTop = (int) (sourceArcBounds.top + intervalWidth);
        int arcBoundsRight = (int) (sourceArcBounds.right - intervalWidth);
        int arcBoundsBottom = (int) (sourceArcBounds.bottom - intervalWidth);
        mTempArcBounds.modify(arcBoundsLeft, arcBoundsTop, arcBoundsRight, arcBoundsBottom);

        return mTempArcBounds;
    }

    private void storeOriginals() {
        mOriginEndDegrees = mEndDegrees;
        mOriginStartDegrees = mEndDegrees;
    }

    private void resetOriginals() {
        mOriginEndDegrees = 0;
        mOriginStartDegrees = 0;

        mEndDegrees = 0;
        mStartDegrees = 0;

        mSwipeDegrees = 0;
    }
}
